﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using Job.Core.Common;

namespace Job.Core.Threading.Task
{
    /// <summary>
    /// 任务运行时引擎。
    /// </summary>
    public class TaskRuntime
    {
        #region Properties

        private List<TaskJob> tasks = new List<TaskJob>();

        /// <summary>
        /// 获取引擎要执行的所有任务。
        /// </summary>
        public List<TaskJob> Tasks { get { return tasks; } }

        private int period = 1000;

        /// <summary>
        /// 运行时跳动周期(毫秒)，默认为1000。
        /// </summary>
        public int Period
        {
            get { return period; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException("Period");

                period = value;
            }
        }

        private Timer timer;

        #endregion

        #region Methods

        /// <summary>
        /// 开始任务引擎。
        /// </summary>
        public void Start()
        {
            timer = new Timer(new TimerCallback(StartWork), null, Period, Period);
        }

        /// <summary>
        /// 停止任务引擎。
        /// </summary>
        public void Stop()
        {
            timer.Dispose();
            timer = null;
        }

        /// <summary>
        /// 释放资源。
        /// </summary>
        /// <param name="ar">ar。</param>
        protected void TaskAsyncCallback(IAsyncResult ar)
        {
            ((TaskAsyncCaller)ar.AsyncState).EndInvoke(ar);
        }


        /// <summary>
        /// 开始执行工作。
        /// </summary>
        /// <param name="state">state。</param>
        protected virtual void StartWork(object state)
        {
            DateTime now = DateTime.Now;

            foreach (TaskJob task in Tasks)
            {
                if (task.IsOnTime(now))
                {
                    TaskAsyncCaller caller = new TaskAsyncCaller(task.Execute);
                    caller.BeginInvoke(now, TaskAsyncCallback, caller);
                }
            }
        }

        /// <summary>
        /// 添加任务。
        /// </summary>
        /// <param name="taskName">任务名称。</param>
        /// <param name="executer">执行者。</param>
        /// <param name="absoluteTime">执行时间。</param>
        /// <param name="logger">日志记录器。</param>
        /// <param name="args">执行参数。</param>
        public void AddTask(string taskName, ITaskExecuter executer, DateTime absoluteTime, ILogger logger, params object[] args)
        {
            if (string.IsNullOrEmpty(taskName))
                throw new ArgumentNullException("taskName");

            if (Tasks.FirstOrDefault(job => job.TaskName == taskName) != null)
                throw new ArgumentException("taskName already exists.");

            if (executer == null)
                throw new ArgumentNullException("executer");

            TaskJob task = new TaskJob
            {
                TaskName = taskName,
                TaskExecuter = executer,
                StartTime = absoluteTime,
                Repeat = 1,
                Logger = logger,
                Params = args
            };

            Tasks.Add(task);
        }

        /// <summary>
        /// 添加任务。
        /// </summary>
        /// <param name="taskName">任务名称。</param>
        /// <param name="executer">执行者。</param>
        /// <param name="startTime">开始时间。</param>
        /// <param name="repeat">循环次数，为null时为无限循环。</param>
        /// <param name="period">周期值。</param>
        /// <param name="periodType">周期类型。</param>
        /// <param name="logger">日志记录器。</param>
        /// <param name="args">执行参数。</param>
        public void AddTask(string taskName, ITaskExecuter executer, DateTime startTime, int? repeat, int period, PeriodType periodType, ILogger logger, params object[] args)
        {
            if (string.IsNullOrEmpty(taskName))
                throw new ArgumentNullException("taskName");

            if (Tasks.FirstOrDefault(job => job.TaskName == taskName) != null)
                throw new ArgumentException("taskName already exists.");

            if (executer == null)
                throw new ArgumentNullException("executer");

            TaskJob task = new TaskJob
            {
                TaskName = taskName,
                TaskExecuter = executer,
                StartTime = startTime,
                Repeat = repeat,
                Period = period,
                PeriodType = periodType,
                Logger = logger,
                Params = args
            };

            Tasks.Add(task);
        }

        /// <summary>
        /// 添加任务。
        /// </summary>
        /// <param name="taskName">任务名称。</param>
        /// <param name="executer">执行者。</param>
        /// <param name="repeat">循环次数，为 null 时为无限循环。</param>
        /// <param name="period">周期值。</param>
        /// <param name="periodType">周期类型。</param>
        /// <param name="logger">日志记录器。</param>
        /// <param name="args">执行参数。</param>
        public void AddTask(string taskName, ITaskExecuter executer, int? repeat, int period, PeriodType periodType, ILogger logger, params object[] args)
        {
            AddTask(taskName, executer, DateTime.Now, repeat, period, periodType, logger, args);
        }

        /// <summary>
        /// 根据任务名称返回指定的任务信息。
        /// </summary>
        /// <param name="taskName">任务名称。</param>
        /// <returns>工作任务实例。</returns>
        public TaskJob GetTask(string taskName)
        {
            return Tasks.FirstOrDefault(job => job.TaskName == taskName);
        }

        /// <summary>
        /// 移除指定名称的任务。
        /// </summary>
        /// <param name="task">任务名称。</param>
        public void Remove(string taskName)
        {
            TaskJob task = GetTask(taskName);

            if (task != null)
                Tasks.Remove(task);
        }        

        #endregion
    }

    /// <summary>
    /// 用于异步任务调用的委托。
    /// </summary>
    /// <param name="time">执行时间。</param>
    public delegate void TaskAsyncCaller(DateTime time);
}